package com.fitbet.wechat.web.interceptor;

import java.util.Date;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.utils.StringUtils;
import com.demo2do.core.web.handler.CookieHandler;
import com.demo2do.core.web.utils.WebUtils;
import com.fitbet.wechat.Constant;

/**
 * 
 * @author mohanlan
 *
 */
public class UserContextInterceptor extends HandlerInterceptorAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class);
    
    @Value("#{config['tencent.host']}")
    private String tencentHost;
    
    @Value("#{config['tencent.mock']}")
    private String tencentMock;
    
    private static final String USER_AGENT = "User-Agent";
    
    private static final String MICRO_MESSENGER = "MicroMessenger";
    
    private static final String OPENID_KEY = Constant.OPENID_KEY;
    
    private static final String UNIONID = Constant.UNIONID_KEY;
    
    public static final String USER_ACCOUNT_KEY = Constant.USER_ACCOUNT_KEY;
    
    public static final int EXPIRE_TIME = 1080 * 24 * 3600;
    
    @Autowired
    private CookieHandler cookieHandler;
    
    /**
     * URL to display general page describes "require WeChat browser environment"
     */
    private String wechatRequiredUrl;
    
    /**
     * URL for wechat auth
     */
    private String wechatAuthUrl;
    
    /**
     * 
     * @param wechatRequiredUrl the wechatRequiredUrl to set
     */
    public void setWechatRequiredUrl(String wechatRequiredUrl) {
        this.wechatRequiredUrl = wechatRequiredUrl;
    }
    
    /**
     * 
     * @param wechatAuthUrl the wechatAuthUrl to set
     */
    public void setWechatAuthUrl(String wechatAuthUrl) {
        this.wechatAuthUrl = wechatAuthUrl;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        
        if (handler instanceof HandlerMethod) {
            
            if(logger.isDebugEnabled()) {
                logger.debug("MobileSniffingInterceptor#preHandle() - To determine this request can be visited in addition to wechat client");
            }
            
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            
            UserContext userContext = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), UserContext.class);
            
            // NONE: do nothing, return super
            if(userContext == null || userContext.type().equals(UserContextType.NONE)) {
                return super.preHandle(request, response, handler);
            }
            

            if(org.apache.commons.lang3.StringUtils.equalsIgnoreCase(tencentMock, "true")) {
                cookieHandler.addCookie(response, Constant.MOBILE_KEY, "17621360181", EXPIRE_TIME);
                cookieHandler.addCookie(response, Constant.UNIONID_KEY, "o3cW3uJid5zYWd--acFDf-GvPgwQ", EXPIRE_TIME);
                cookieHandler.addCookie(response, Constant.OPENID_KEY, "o98VLv2PZlRxhmhKR0MP1yE8-Jss", EXPIRE_TIME);
                cookieHandler.addCookie(response, Constant.USER_ACCOUNT_KEY, "11", EXPIRE_TIME);
            }
            
            logger.trace("#preHandle - start user constent interceptor");
            
            String originUrl = this.generateOriginUrl(request, response);
            logger.trace("#preHandle - origin url [{}]", originUrl);
            
            String redirectUrl = null;
            
            
            // WECHAT_REQUIRED: check enviroment and redirect to wechat 
            //                  required page if env is microMessenger
            if(userContext.type().equals(UserContextType.WECHAT_REQUIRED)) {
                if(!this.isMicroMessenger(request)) {
                    String wechatRequiredUrl = this.wechatRequiredUrl.startsWith("http") ? this.wechatRequiredUrl : StringUtils.connect(WebUtils.getRealServerPath(request), this.wechatRequiredUrl);
                    redirectUrl = wechatRequiredUrl;
                }
            
            // WECHAT_BASE: check env & cookie,
            //              if env is microMessenger & openid cookie not exit
            //              redirect to wechat base auth url
            } else if(userContext.type().equals(UserContextType.WECHAT_BASE)) {
                if(this.needWechatBaseAuth(request)) {
                    redirectUrl = this.generateAuthPrepare(originUrl, "snsapi_base", request);
                }
                
            // WECHAT_AUTHORITY: check env & cookie,
            //                   if env is microMessenger & openid cookie not exit 
            //                   & user account cookie not exit
            //                   redirect to wechat authority url
            } else if(userContext.type().equals(UserContextType.WECHAT_AUTHORITY)) {
                
                if(this.needWechatAuthority(request)) {
                    
                    if(!this.isMicroMessenger(request)) {
                        String wechatRequiredUrl = this.wechatRequiredUrl.startsWith("http") ? this.wechatRequiredUrl : StringUtils.connect(WebUtils.getRealServerPath(request), this.wechatRequiredUrl);
                        redirectUrl = wechatRequiredUrl;
                    }
                    
                    logger.info("#prehandler() -- start wedchat authority");
                    redirectUrl = this.generateAuthPrepare(originUrl, "snsapi_userinfo", request);
                }
            }
            
            // FINAL: if redirect url exit, do redirect here
            if(org.apache.commons.lang3.StringUtils.isNoneEmpty(redirectUrl)) {
                response.sendRedirect(redirectUrl);
                return false;
            }
        }
        
        return super.preHandle(request, response, handler);
    }
    
    /**
     * 
     * @param request
     * @param response
     * @return
     */
    private String generateOriginUrl(HttpServletRequest request, HttpServletResponse response) {
        
        String client = WebUtils.getRealServerPath(request);
        
        logger.info("client is [{}]",client);
        
        String originalUri = WebUtils.getRealRequestURI(request);
        
        logger.info("originalUri is [{}]",originalUri);
        
        String queryString = request.getQueryString();
        
        logger.info("queryString is [{}]",queryString);
        
        String originalUrl = StringUtils.connect(client, org.apache.commons.lang3.StringUtils.isBlank(queryString) ? originalUri : StringUtils.connect(originalUri, "?", queryString));
        
        logger.info("originalUrl is [{}]",originalUrl);
        
        return WebUtils.encode(originalUrl, "UTF-8");
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean isMicroMessenger(HttpServletRequest request) {
        
        String userAgent = request.getHeader(USER_AGENT);
        logger.info("#isMicroMessenger() - Get user agent [{}].", userAgent);
        
        return org.apache.commons.lang3.StringUtils.isNoneBlank(userAgent) && userAgent.indexOf(MICRO_MESSENGER) >= 0;
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean needWechatBaseAuth(HttpServletRequest request) {
        
        // validate env
        if(!this.isMicroMessenger(request)) {
            return false;
        }
        
        // validate openid cookie
        Cookie openidCookie = org.springframework.web.util.WebUtils.getCookie(request, OPENID_KEY);
        if(openidCookie != null) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean needWechatAuthority(HttpServletRequest request) {
        
        // validate openid cookie
        Cookie openidCookie = org.springframework.web.util.WebUtils.getCookie(request, OPENID_KEY);
        logger.info("get openidCookie [{}]", JsonUtils.toJson(openidCookie));
        logger.info("get openidCookie [{}]", openidCookie == null ? "null" : openidCookie.getValue());
        
        // validate unionid cookie
        Cookie unionidCookie = org.springframework.web.util.WebUtils.getCookie(request, UNIONID);
        logger.info("get unionidCookie [{}]", JsonUtils.toJson(unionidCookie));
        logger.info("get unionidCookie [{}]", unionidCookie == null ? "null" : unionidCookie.getValue());
        
        if(openidCookie != null && org.apache.commons.lang3.StringUtils.isNotBlank(openidCookie.getValue()) && !org.apache.commons.lang3.StringUtils.equalsIgnoreCase(openidCookie.getValue(), "null") &&
           unionidCookie != null && org.apache.commons.lang3.StringUtils.isNotBlank(unionidCookie.getValue()) && !org.apache.commons.lang3.StringUtils.equalsIgnoreCase(unionidCookie.getValue(), "null")) {
            return false;
        } else {
            return true;
        }
    }
    
    /**
     * 
     * @param originUrl
     * @param type
     * @return
     */
    private String generateAuthPrepare(String originUrl, String type, HttpServletRequest request) {
        
        String authPrepare = this.wechatAuthUrl.startsWith("http") ? 
                               wechatAuthUrl : 
                               StringUtils.connect(tencentHost + wechatAuthUrl);
        
        String client = com.demo2do.core.web.utils.WebUtils.getRealServerPath(request);
        
        String state = String.valueOf(new Date().getTime());
        
        return StringUtils.connect(authPrepare, 
                                   "?scope=", type, 
                                   "&redirectUrl=", originUrl,
                                   "&client=", client,
                                   "&requestNo=", state);
    }
}
